import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Control, ControlProps, componentsDir } from 'components/common';
import { guid, templateControlIds } from 'share/common';
import { StationService } from 'user/services/stationService';
import { } from 'admin/application';
import { UserPage as UserPage } from 'user/application';
import { errors } from 'maishu-dilu';
import { ValueStore } from 'maishu-chitu';


export type MobilePageControl = Control<any, any> & { id?: string, controlName: string }

export interface Props extends React.Props<MobilePage> {
    pageData: PageData;
    template?: PageData;
    elementPage: UserPage;
    designTime?: {
        controlSelected?: (
            control: MobilePageControl,
            controlType: React.ComponentClass<any>
        ) => void
    };
    controlCreated?: (control: MobilePageControl, isSelected: boolean) => void,
    enableMock?: boolean,
    styleColor: StyleColor,
}

export interface ControlDescription {
    controlId: string;
    controlName: string;
    data?: any;
    selected?: boolean | 'disabled';
}

export type ControlPair = { control: MobilePageControl, controlType: React.ComponentClass<any> }
type State = {
    template?: PageData,
    typesLoaded: boolean,
};

export let MobilePageContext = React.createContext<{ components: ValueStore<React.Component[]> }>({ components: null });

/**
 * 移动端页面，将 PageData 渲染为移动端页面。
 */
export class MobilePage extends React.Component<Props, State>{
    private footerElement: HTMLElement;
    private headerElement: HTMLElement;

    components = new ValueStore<React.Component[]>([])
    private _controls: (Control<any, any> & { controlId: string, controlName: string })[];
    selecteControl: { control: MobilePageControl, controlType: React.ComponentClass<any> };
    constructor(props) {
        super(props);
        this.state = {
            typesLoaded: false
        };

        this._controls = [];
        this.loadControlTypes(this.props.pageData)
    }

    loadControlTypes(pageData: PageData) {
        if (pageData == null) throw errors.argumentNull('pageData')

        let allTypeLoad = pageData.controls.map(o => {
            return MobilePage.getControlType(o.controlName)
        })
        Promise.all(allTypeLoad)
            .then(arr => {
                this.setState({ typesLoaded: true })
            })
            .catch(e => {
                //TODO
            })
    }


    get controls() {
        return this._controls;
    }

    static getInstanceByElement(element: HTMLElement): MobilePage {
        return (element as any).mobilePage;
    }

    /**
     * 创建控件
     * @param controlData 描述控件的数据
     * @param element 承载控件的 HTML 元素
     */
    async createControlInstance(controlData: ControlDescription, element: HTMLElement): Promise<ControlPair> {
        let { controlId, controlName, data, selected } = controlData;
        let type = await MobilePage.getControlType(controlName);
        console.assert(type != null)
        let props: ControlProps<any> = Object.assign({}, data || {});
        props.mobilePage = this;
        console.assert(this.props.elementPage != null);

        let reactElement = React.createElement(type.Control, props);
        let control: MobilePageControl = ReactDOM.render(reactElement, element);
        console.assert(control != null)
        element.className = `${controlName}-control`;
        control.id = controlId;
        control.controlName = controlName;

        let result: ControlPair = { control, controlType: type.Control };

        let components = this.components.value
        components.push(control)
        this.components.value = components;

        if (controlData.selected) {
            this.selecteControl = { control: control, controlType: type.Control };
        }

        return result;
    }

    /**
     * 获取控件在类型
     * @param controlName 控件的名称
     */
    static getControlType(controlName: string): Promise<{ Name: string, Control: React.ComponentClass<any>, Props: { new() } }> {

        let arr = controlName.split(':');
        let fileName = arr[0];
        let name = arr[1] || 'default';
        let filePath = `${componentsDir}/${fileName}/control`;
        return new Promise((resolve, reject) => {
            requirejs([filePath], function (exports) {
                resolve({ Name: controlName, Control: exports[name], Props: exports.Props });
            })
        })
    }

    renderControls(controls: ControlDescription[]) {
        if (this.props.designTime) {
            return this.renderDesigntimeControls(controls);
        }

        return this.renderRuntimeControls(controls);
    }

    async componentDidMount() {
        if (this.props.pageData.templateId != null) {
            let station = this.props.elementPage.createService(StationService);
            let template = await station.pages.pageDataById(this.props.pageData.templateId);
            this.setState({ template })
        }
    }

    renderRuntimeControls(controls: ControlDescription[]) {
        controls = controls || [];
        return controls.map((o, i) =>
            <div id={o.controlId} key={o.controlId}
                ref={async (e: HTMLElement) => {
                    if (!e) return;
                    var c = await this.createControlInstance(o, e);
                    var componet = Object.assign(c.control, { controlId: o.controlId, controlName: o.controlName });
                    this.controls.push(componet);
                    if (this.props.controlCreated) {
                        let selected: boolean = o.selected == true ? true : false;
                        this.props.controlCreated(componet, selected);
                    }
                }} />
        );
    }
    renderDesigntimeControls(controls: ControlDescription[]) {
        controls = controls || [];
        return controls.map((o, i) =>
            <div id={o.controlId} key={o.controlId}
                ref={async (e: HTMLElement) => {
                    if (!e) return;

                    var c = await this.createControlInstance(o, e);
                    var componet = Object.assign(c.control, { controlId: o.controlId, controlName: o.controlName });
                    this.controls.push(componet);
                    if (o.selected != 'disabled') {
                        e.onclick = (event) => {
                            controls.filter(o => o.selected == true).forEach(o => o.selected = false);
                            this.props.designTime.controlSelected(c.control, c.controlType);
                            event.preventDefault();
                        }
                    }

                    if (o.selected == true) {
                    }

                    if (this.props.controlCreated) {
                        let selected = o.selected == true ? true : false;
                        this.props.controlCreated(componet, selected);
                    }

                }} />
        );
    }

    /**
     * 渲染页面的头部
     * @param pageData 页面的数据，用于描述一个页面
     */
    renderHeader(pageData: PageData): JSX.Element {
        let headerControls = pageData.controls.filter(o => o.position == 'header');
        return (
            <header key="header" className="page-header"
                ref={(e: HTMLElement) => this.headerElement = e || this.headerElement}>
                {this.renderControls(headerControls)}
            </header>
        )
    }

    /**
     * 渲染页面的脚
     * @param pageData 页面的数据，用于描述一个页面
     */
    renderFooter(pageData: PageData): JSX.Element {
        let footerControls = pageData.controls.filter(o => o.position == 'footer');
        return (
            <footer key="footer" className="page-footer"
                ref={(e: HTMLElement) => this.footerElement = e || this.footerElement}>
                {this.renderControls(footerControls)}
            </footer>
        )
    }


    /**
     * 渲染页面视图
     * @param pageData 页面数据，用于描述一个页面
     */
    renderView(pageData: PageData) {

        let designMode = this.props.designTime;
        if (designMode) {
            return this.renderDesigntimeViews(pageData);
        }

        return this.renderRuntimeViews(pageData);
    }

    renderRuntimeViews(pageData: PageData) {
        let viewControls = pageData.controls.filter(o => o.position == 'view');
        return <section key={`view`} className="page-view"
            ref={(e: HTMLElement) => {
                if (!e) return;
                this.setPageElementClassName(e, pageData);
                setTimeout(() => {
                    if (this.footerElement) {
                        let height = this.footerElement.offsetHeight;
                        e.style.paddingBottom = `${height}px`;
                    }

                    if (this.headerElement) {
                        let height = this.headerElement.offsetHeight;
                        e.style.paddingTop = `${height}px`;
                    }

                }, 500);
            }}>
            {this.renderControls(viewControls)}
        </section>
    }

    private setPageElementClassName(viewElement: HTMLElement, pageData: PageData) {
        console.assert(viewElement != null);

        let pageElement = viewElement.parentElement;
        console.assert(pageElement != null);

        let className = pageElement.className;
        if (pageData.className && className.indexOf(pageData.className) < 0) {
            className = className + ' ' + pageData.className;
            pageElement.className = className;
        }
    }

    get styleColor(): StyleColor {
        return this.props.styleColor;
    }

    renderDesigntimeViews(pageData: PageData) {
        let sortableElement = (element: HTMLElement) => {
            type UI = { item: JQuery, placeholder: JQuery, helper: JQuery };

            let newControlIndex: number = 0;
            $(element).sortable({
                axis: "y",
                change: () => {
                    for (let i = 0; i < element.children.length; i++) {
                        if (!element.children.item(i).id) {
                            newControlIndex = i;
                            break;
                        }
                    }
                },
                receive: (event: Event, ui: UI) => {
                    let helper = ui.helper[0] as HTMLElement;
                    helper.removeAttribute('style');
                    let controlName = ui.item.attr('data-control-name');
                    let target = ui.item.attr('data-target');
                    console.assert(controlName != null);
                    ui.helper.remove();
                    if (target == 'footer')
                        pageData.controls.push({ controlId: guid(), controlName, data: {}, position: 'footer' });
                    else if (target == 'header')
                        pageData.controls.push({ controlId: guid(), controlName, data: {}, position: 'header' });
                    else {
                        let children = element.children;
                        console.assert(newControlIndex != null);
                        pageData.controls.splice(newControlIndex + 1, 0, { controlId: guid(), controlName, data: {}, position: 'view' });
                        newControlIndex = null;
                    }

                    this.setState(this.state);
                },
                update: (event, ui) => {
                    let view_controls = [];
                    let footer_controls = [];
                    //===================================================
                    // 排序 view controls
                    for (let i = 0; i < element.children.length; i++) {
                        let child = element.children[i] as HTMLElement;
                        let control = pageData.controls.filter(o => o.controlId == child.id && o.position == 'view')[0];
                        console.assert(control != null);
                        view_controls[i] = control;
                    }
                    //===================================================
                    for (let i = 0; i < this.footerElement.children.length; i++) {
                        let child = this.footerElement.children[i] as HTMLElement;
                        let control = pageData.controls.filter(o => o.controlId == child.id && o.position == 'footer')[0];
                        footer_controls[i] = control;
                    }
                    //===================================================

                    // pageData.view.controls = view_controls;
                    // pageData.footer.controls = footer_controls;
                    let header_controls = pageData.controls.filter(o => o.position == 'header');
                    pageData.controls = [...header_controls, ...footer_controls, ...view_controls];
                }
            })
        }

        return <section key='view'
            ref={(e: HTMLElement) => {
                if (!e) return;
                sortableElement(e);
                this.setPageElementClassName(e, pageData);
                setTimeout(() => {
                    if (this.footerElement) {
                        let height = this.footerElement.offsetHeight;
                        e.style.paddingBottom = `${height}px`;
                    }

                    if (this.headerElement) {
                        let height = this.headerElement.offsetHeight;
                        e.style.paddingTop = `${height}px`;
                    }

                }, 500);
            }}
        >
            {this.renderControls(pageData.controls.filter(o => o.position == 'view'))}
        </section>
    }

    merge(pageData: PageData, productTemplate: PageData) {
        console.assert(productTemplate != null);
        console.assert(pageData != null);
        pageData.controls = pageData.controls.filter(o => o.save != false);
        //===========================================================
        // 复制一份数据，不要在源数据上修改
        productTemplate = JSON.parse(JSON.stringify(productTemplate));
        // pageData = JSON.parse(JSON.stringify(pageData));
        //===========================================================
        // 模板里面的控件，不需要保存
        productTemplate.controls.forEach(o => o.save = false);

        for (let i = 0; i < pageData.controls.length; i++) {
            let sourceControl = pageData.controls[i];
            let templateControlId = templateControlIds[sourceControl.controlName];
            let controlIndex: number;
            if (templateControlId != null) {
                for (let i = 0; i < productTemplate.controls.length; i++) {
                    if (productTemplate.controls[i].controlId == templateControlId) {
                        controlIndex = i;
                        break;
                    }
                }
            }

            let existsControl = productTemplate.controls.filter(o => o.controlId == sourceControl.controlId)[0];
            if (existsControl) {
                continue;
            }

            if (controlIndex != null) {
                let i = controlIndex;
                productTemplate.controls[i] = sourceControl;
            }
            else {
                let otherControl = productTemplate.controls
                    .map((o, i) => ({ control: o, index: i }))
                    .filter(o => o.control.controlId == templateControlIds.other)[0]

                if (otherControl != null) {
                    productTemplate.controls.splice(otherControl.index, 0, sourceControl);
                }
                else {
                    productTemplate.controls.push(sourceControl);
                }
            }
        }
        productTemplate.controls = productTemplate.controls.filter(o => o.controlId != templateControlIds.other);
        pageData.controls = productTemplate.controls;
    }

    render() {
        if (!this.state.typesLoaded) {
            return <div className="text-center" style={{ paddingTop: 150 }}>
                页面正在加载中...
            </div>
        }
        let { pageData, template } = this.props;
        if (template) {
            this.merge(pageData, template);
        }

        var result = [
            this.renderHeader(pageData),
            this.renderFooter(pageData),
            this.renderView(pageData),
        ];

        // if (this.props.designTime && this.props.designTime.controlSelected) {
        //     // 加上延时，否则编辑器有可能显示不出来
        //     setTimeout(() => {
        //         if (this.selecteControl != null) {
        //             let c = this.selecteControl;
        //             this.props.designTime.controlSelected(c.control, c.controlType);
        //         }
        //     }, 500);
        // }

        return result;

    }
}

